Skip to content

Unify traversal/cache refactors, hierarchy path context, and compact edge ID conversions#193

Merged
renau merged 1 commit intomasc-ucsc:mainfrom
Sohamkayal4103:backup
Mar 2, 2026
Merged

Unify traversal/cache refactors, hierarchy path context, and compact edge ID conversions#193
renau merged 1 commit intomasc-ucsc:mainfrom
Sohamkayal4103:backup

Conversation

@Sohamkayal4103
Copy link
Contributor

@Sohamkayal4103 Sohamkayal4103 commented Mar 2, 2026

Summary

This PR consolidates recent graph API and traversal work into one clean commit:

  • Iterator/test organization and traversal API alignment
  • Pin/edge direction-role handling fixes (driver/sink bit correctness)
  • Cached forward traversals (forward_class, forward_flat, forward_hier)
  • Hierarchy-path context refactor using tree-backed (hier_ref, hier_pos) in _hier types
  • Compact ID tier extensions and conversions for pins/edges (*_class, *_flat, *_hier)

Why

  • Complete the high-priority API/todo items in a coherent step
  • Make hierarchy traversal context correct and unambiguous across repeated instances
  • Keep traversal results cached and invalidated via graph-library mutation epoch
  • Provide compact conversion paths needed for external attribute-table usage

Key Changes

  • Graph traversal/cache rebuild and invalidation flow updates
  • Node_hier path-context storage migrated to tree-backed hierarchy positions
  • Added compact pin/edge tier types and conversion helpers
  • Expanded iterator/graph tests for role bits, hierarchy behavior, and cache invalidation
  • Bazel test target organization updates

Validation

  • bazel test -c dbg //hhds:iterators_impl //hhds:graph_test

Summary by CodeRabbit

Release Notes

  • New Features

    • Graph elements now support multiple representations (class, flat, and hierarchical) for flexible access patterns
    • Enhanced edge and pin iteration across graph structures
    • New query methods for traversing and organizing graph elements
    • Optimized cache management for improved traversal performance
  • Build Changes

    • Updated C++ standard to C++20

Copilot AI review requested due to automatic review settings March 2, 2026 03:28
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

This PR introduces a multi-representation system for the HHDS graph library, adding hierarchical (Node_hier, Pin_hier), flat (Node_flat, Pin_flat), and class-based (Node_class, Pin_class) node/pin/edge abstractions with automatic conversions, traversal caching mechanisms, and an expanded public Graph API for querying and manipulating graphs. The C++ standard is downgraded from c++23 to c++20.

Changes

Cohort / File(s) Summary
Build Configuration
.bazelrc
C++ standard downgraded from c++23 to c++20 across common, asan, and tsan build configurations.
Build Dependencies
hhds/BUILD.bazel
Added tree.hpp to graph library public headers and @iassert as a public dependency; test targets iterators and iterators_impl renamed and swapped with source files updated correspondingly.
Core Graph Library
hhds/graph.hpp
Introduced multi-representation types: Node_hier, Pin_hier, Node_flat, Pin_flat, Edge_flat, Edge_hier with comparison and hashing support. Expanded Pin_class and Node_class with new constructors and accessors. Added conversion utilities (to_class, to_flat, to_hier) for nodes, pins, and edges. Extended Graph public API with set_subnode() overloads, pin/node querying methods (get_pins, get_driver_pins, get_sink_pins), and lazy traversal accessors (fast_class, forward_class, fast_flat, forward_flat, fast_hier, forward_hier). Added GraphLibrary::mutation_epoch() tracking.
Core Graph Implementation
hhds/graph.cpp
Implemented hierarchical node and pin types with root/current Gid accessors. Added traversal cache management with invalidate_traversal_caches() and cache rebuilders for all representations. Extended edge and node iteration APIs (out_edges, inp_edges for Pin_class). Refactored mutations (clear_graph, create_node, create_pin, add_edge, del_edge, delete_node) to invalidate caches. Added pin/node conversion helpers and inter-graph linking via set_subnode(). Updated edge packing/unpacking logic to align with new mask semantics.
Test Updates
hhds/tests/graph_test.cpp
Migrated from direct node.set_subnode() calls to public graph.set_subnode() API across all subnode relationship setup calls.
Iterator Tests (Rewrite)
hhds/tests/iterators.cpp
Complete rewrite to validate Node_class/Pin_class as primary handles instead of raw Nid/Pid. Added tests for node equality/hashability, edge iteration (out_edges, inp_edges), pin extraction, edge mutation (del_edge, add_edge), and lazy traversal methods (fast_class, fast_flat).
Iterator Implementation Tests
hhds/tests/iterators_impl.cpp
Extensive new test coverage for multi-representation conversions (to_flat, to_class, to_hier for nodes, pins, edges), bit-encoding invariants, edge iteration with pin_pid validation, master node relationships, delayed cache rebuilding, and forward/topological ordering across class/flat/hierarchical representations.

Sequence Diagram

sequenceDiagram
    participant App as Application Code
    participant Graph as Graph Instance
    participant Cache as Traversal Caches
    participant Conv as Conversion Utilities

    App->>Graph: Mutation (add_edge, del_edge, create_node, etc.)
    activate Graph
    Graph->>Cache: invalidate_traversal_caches()
    deactivate Graph
    
    App->>Graph: Query (fast_class(), forward_flat(), etc.)
    activate Graph
    Graph->>Cache: Cache valid?
    alt Cache Invalid
        Graph->>Graph: rebuild_fast_class_cache()
        Graph->>Graph: rebuild_forward_flat_cache()
        Graph->>Graph: rebuild_fast_hier_cache()
        Graph->>Cache: Store rebuilt representations
    end
    Graph->>App: Return span<Node_class/Node_flat/Node_hier>
    deactivate Graph
    
    App->>Conv: to_flat(Node_class, current_gid, root_gid)
    activate Conv
    Conv->>Conv: Extract Nid, port_id, and hierarchy info
    Conv->>App: Return Node_flat
    deactivate Conv
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • #189: Directly overlapping changes to Node_class/Pin_class, create_node() return type, out_edges()/inp_edges(), cache APIs, and del_edge()/add_edge() overloads.
  • #185: Introduces hierarchical and inter-graph traversal features via set_subnode() and GraphLibrary binding that this PR extends with mutation tracking and multi-representation support.
  • #180: Adds Node::set_subnode() and GraphLibrary/Gid types that are refactored here into public Graph::set_subnode() overloads and mutation epoch tracking.

Poem

🐰 A graph that dances through many forms,
From class to flat to hier, it transforms!
Caches spring alive when mutations call,
Conversions leap betwixt them all—
Now nodes and pins in triples play! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the three main changes: traversal/cache refactors, hierarchy path context unification, and compact edge ID conversions, accurately summarizing the PR's primary objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
hhds/tests/iterators.cpp (1)

1-12: Note: Active tests overlap with iterators_impl.cpp.

Several active tests in this file (CompactTypes, EdgeIteration, DelEdge, LazyTraversal, AddEdgeShorthand) have equivalent implementations in iterators_impl.cpp. Given the manual tag and file header indicating this is for "planned API" documentation, this duplication appears intentional. The active tests may have been kept to demonstrate the API surface even though they're not run in CI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hhds/tests/iterators.cpp` around lines 1 - 12, This file contains active
tests (CompactTypes, EdgeIteration, DelEdge, LazyTraversal, AddEdgeShorthand)
that duplicate runnable implementations in iterators_impl.cpp; to resolve the
duplication, disable or remove the active tests here so this file only documents
planned (commented-out) APIs: locate the test cases named CompactTypes,
EdgeIteration, DelEdge, LazyTraversal and AddEdgeShorthand and either comment
them out or mark them with the same manual/disabled attribute used elsewhere
(matching iterators_impl.cpp's approach), ensuring their names remain in
comments as API docs but are not compiled or executed.
hhds/BUILD.bazel (1)

300-321: Clarify test naming swap intent.

The test targets have been swapped: what was iterators (with iterators.cpp) is now iterators_impl, and vice versa. Additionally, the new iterators test has a manual tag with comment "planned API, not yet implemented".

This naming may be confusing since iterators_impl.cpp contains implemented tests but is now named iterators_impl, while the pending API tests in iterators.cpp are named iterators. Consider documenting this naming convention or using more descriptive names like iterators_active and iterators_planned.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hhds/BUILD.bazel` around lines 300 - 321, The Bazel test targets were swapped
and are confusing: the target named "iterators_impl" builds
tests/iterators_impl.cpp (implemented tests) while "iterators" builds
tests/iterators.cpp (planned API) and is tagged manual; either rename the
targets to make intent explicit (e.g., "iterators_active" for
tests/iterators_impl.cpp and "iterators_planned" for tests/iterators.cpp) or add
a clear comment above the cc_test rules explaining that "iterators_impl"
contains implemented tests and "iterators" is a manual/planned API test; update
the names or comment in BUILD.bazel where the cc_test targets "iterators_impl"
and "iterators" are defined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hhds/graph.hpp`:
- Around line 472-491: The fast-flat cache is missing a validity flag and epoch
so fast_flat() always rebuilds; add a mutable bool fast_flat_cache_valid_ =
false and a mutable uint64_t fast_flat_cache_epoch_ = 0 alongside the existing
cache members (fast_flat_cache_), then update rebuild_fast_flat_cache() to check
the epoch/valid flag before rebuilding and to set fast_flat_cache_valid_ = true
and update fast_flat_cache_epoch_ after a rebuild; also ensure any cache
invalidation paths that touch other caches (where they clear fast_flat_cache_ or
bump epochs) properly clear fast_flat_cache_valid_ or increment
fast_flat_cache_epoch_ so the fast_flat cache can be reused when valid.

---

Nitpick comments:
In `@hhds/BUILD.bazel`:
- Around line 300-321: The Bazel test targets were swapped and are confusing:
the target named "iterators_impl" builds tests/iterators_impl.cpp (implemented
tests) while "iterators" builds tests/iterators.cpp (planned API) and is tagged
manual; either rename the targets to make intent explicit (e.g.,
"iterators_active" for tests/iterators_impl.cpp and "iterators_planned" for
tests/iterators.cpp) or add a clear comment above the cc_test rules explaining
that "iterators_impl" contains implemented tests and "iterators" is a
manual/planned API test; update the names or comment in BUILD.bazel where the
cc_test targets "iterators_impl" and "iterators" are defined.

In `@hhds/tests/iterators.cpp`:
- Around line 1-12: This file contains active tests (CompactTypes,
EdgeIteration, DelEdge, LazyTraversal, AddEdgeShorthand) that duplicate runnable
implementations in iterators_impl.cpp; to resolve the duplication, disable or
remove the active tests here so this file only documents planned (commented-out)
APIs: locate the test cases named CompactTypes, EdgeIteration, DelEdge,
LazyTraversal and AddEdgeShorthand and either comment them out or mark them with
the same manual/disabled attribute used elsewhere (matching iterators_impl.cpp's
approach), ensuring their names remain in comments as API docs but are not
compiled or executed.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0536c14 and 92e7ada.

📒 Files selected for processing (7)
  • .bazelrc
  • hhds/BUILD.bazel
  • hhds/graph.cpp
  • hhds/graph.hpp
  • hhds/tests/graph_test.cpp
  • hhds/tests/iterators.cpp
  • hhds/tests/iterators_impl.cpp

Comment on lines +472 to +491
std::vector<Node> node_table;
std::vector<Pin> pin_table;
mutable std::vector<Node_class> fast_class_cache_;
mutable std::vector<Node_flat> fast_flat_cache_;
mutable std::vector<Node_hier> fast_hier_cache_;
mutable std::vector<Node_class> forward_class_cache_;
mutable std::vector<Node_flat> forward_flat_cache_;
mutable std::vector<Node_hier> forward_hier_cache_;
mutable std::shared_ptr<tree<Gid>> fast_hier_tree_cache_;
mutable std::shared_ptr<tree<Gid>> forward_hier_tree_cache_;
mutable bool fast_class_cache_valid_ = false;
mutable bool fast_hier_cache_valid_ = false;
mutable bool forward_class_cache_valid_ = false;
mutable bool forward_flat_cache_valid_ = false;
mutable bool forward_hier_cache_valid_ = false;
mutable uint64_t fast_hier_cache_epoch_ = 0;
mutable uint64_t forward_flat_cache_epoch_ = 0;
mutable uint64_t forward_hier_cache_epoch_ = 0;
const GraphLibrary* owner_lib_ = nullptr;
Gid self_gid_ = Gid_invalid;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing fast_flat_cache_valid_ flag.

The cache validity flags include fast_class_cache_valid_, fast_hier_cache_valid_, forward_class_cache_valid_, forward_flat_cache_valid_, and forward_hier_cache_valid_, but there's no fast_flat_cache_valid_ flag. Looking at the implementation in graph.cpp, fast_flat() always calls rebuild_fast_flat_cache() unconditionally (line 1110), meaning the cache is never reused.

Consider adding a validity flag and epoch tracking for fast_flat cache similar to the other caches if cache reuse is intended.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hhds/graph.hpp` around lines 472 - 491, The fast-flat cache is missing a
validity flag and epoch so fast_flat() always rebuilds; add a mutable bool
fast_flat_cache_valid_ = false and a mutable uint64_t fast_flat_cache_epoch_ = 0
alongside the existing cache members (fast_flat_cache_), then update
rebuild_fast_flat_cache() to check the epoch/valid flag before rebuilding and to
set fast_flat_cache_valid_ = true and update fast_flat_cache_epoch_ after a
rebuild; also ensure any cache invalidation paths that touch other caches (where
they clear fast_flat_cache_ or bump epochs) properly clear
fast_flat_cache_valid_ or increment fast_flat_cache_epoch_ so the fast_flat
cache can be reused when valid.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates graph traversal/cache refactors and compact ID tier conversions, including a new tree-backed hierarchy context for _hier types and expanded traversal/iterator test coverage.

Changes:

  • Added _flat / _hier compact types for pins/edges plus conversion helpers (to_class / to_flat / to_hier).
  • Implemented cached forward traversals (forward_class, forward_flat, forward_hier) and cached hierarchy traversal (fast_hier) with invalidation via GraphLibrary::mutation_epoch().
  • Reorganized/expanded tests for compact conversions, bit-encoding contracts, hierarchy instance disambiguation, and cache invalidation.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
hhds/tests/iterators_impl.cpp Adds/extends iterator/traversal and compact conversion tests, including cache invalidation and hierarchy instance behavior.
hhds/tests/iterators.cpp Reorganizes planned/manual tests and keeps a smaller implemented subset (most prior sections commented out).
hhds/tests/graph_test.cpp Updates hierarchy setup to use the new Graph::set_subnode(...) API.
hhds/graph.hpp Introduces new compact tier types (Pin_flat, Node_hier, Pin_hier, Edge_flat, Edge_hier), new traversal APIs, and mutation epoch plumbing.
hhds/graph.cpp Implements hierarchy context access, conversions, traversal caches, new pin/edge APIs, and cache invalidation behavior.
hhds/BUILD.bazel Updates targets/test organization and adds tree.hpp / @iassert dependency wiring.
.bazelrc Changes default C++ standard flags from C++23 to C++20 (including asan/tsan configs).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +268 to 274
constexpr int SHIFT = 14;
constexpr uint64_t SLOT = (1ULL << SHIFT) - 1;
for (int i = 0; i < 4; ++i) {
// Extract the i-th packed slot from sedges_.sedges
uint64_t packed = (static_cast<uint64_t>(pin->sedges_.sedges) >> (i * SHIFT)) & SLOT_MASK;
if (packed == 0) {
continue;
}
if ((packed >> 2) == (static_cast<uint64_t>(other_id) >> 2)) {
// Clear the matching slot
pin->sedges_.sedges &= ~(static_cast<int64_t>(SLOT_MASK) << (i * SHIFT));
uint64_t e = edge & (SLOT << (i * SHIFT));
if (e >> 2 == (other_id >> 2)) {
pin->sedges_.sedges &= ~(static_cast<int64_t>(SLOT) << (i * SHIFT));
return true;
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pin::delete_edge() packed-edge removal logic is incorrect: it masks edge (the decoded Vid) instead of scanning/extracting slots from pin->sedges_.sedges. As written, the loop will almost never match the stored packed slots, so deletions can silently fail (and leave stale packed edges). Rework this loop to extract each packed slot from sedges_.sedges (as before) and clear the matching slot when its decoded target matches other_id.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +103
auto to_flat(const Edge_class& e, Gid current_gid, Gid root_gid) -> Edge_flat {
if (root_gid == Gid_invalid) {
root_gid = current_gid;
}
Edge_flat out;
out.driver = to_flat(e.driver_pin, current_gid, root_gid);
out.sink = to_flat(e.sink_pin, current_gid, root_gid);
return out;
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_flat(const Edge_class&, ...) currently always converts e.driver_pin/e.sink_pin, but Edge_class instances for n->n, n->p, and p->n (e.g. from out_edges(Node_class)/inp_edges(Node_class)) leave one or both Pin_class fields default. This will produce Edge_flat values with pin_pid=0 and lose endpoint information. Consider either (a) restricting this conversion to Edge_class::type == 2 (p->p) with a clear runtime check/assert, or (b) encoding node endpoints into the flat tier so all edge types round-trip safely.

Copilot uses AI. Check for mistakes.
Comment on lines +1315 to +1316
// Driver pin: edge is outgoing (bit1=0) and target is a pin (bit0=1).
if (!(vid & static_cast<Vid>(2)) && (vid & static_cast<Vid>(1))) {
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_driver_pins() only treats a pin as a driver if it has an outgoing edge to another pin (bit0=1). Pins that drive a node via p->n edges (outgoing, bit2=0 but bit0=0) will be omitted even though out_edges(Pin_class) supports p->n. If driver-ness is meant to include p->n, the condition should be based on direction (bit2) rather than requiring bit0=1.

Suggested change
// Driver pin: edge is outgoing (bit1=0) and target is a pin (bit0=1).
if (!(vid & static_cast<Vid>(2)) && (vid & static_cast<Vid>(1))) {
// Driver pin: edge is outgoing (direction bit set to 0), regardless of target type.
if (!(vid & static_cast<Vid>(2))) {

Copilot uses AI. Check for mistakes.
Comment on lines +1333 to +1334
// Sink pin: edge is incoming (bit1=1) and source is a pin (bit0=1).
if ((vid & static_cast<Vid>(3)) == static_cast<Vid>(3)) {
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_sink_pins() only treats a pin as a sink if it has an incoming edge from another pin (vid&3==3). Pins that are driven by a node via n->p edges (incoming, bit2=1 but bit0=0) will be omitted. If sink-ness is meant to include n->p, broaden the condition to accept any incoming edge (bit2=1), not only pin sources.

Suggested change
// Sink pin: edge is incoming (bit1=1) and source is a pin (bit0=1).
if ((vid & static_cast<Vid>(3)) == static_cast<Vid>(3)) {
// Sink pin: edge is incoming (bit1=1), regardless of whether the source is a pin or a node.
if (vid & static_cast<Vid>(2)) {

Copilot uses AI. Check for mistakes.
@renau renau merged commit 16c1495 into masc-ucsc:main Mar 2, 2026
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants